#pragma once

using namespace System;
using namespace System::Collections::Generic;
using namespace System::IO;
using namespace NUnit::Framework;
using namespace LatoolNet;

namespace LatoolNetTest {

	[TestFixture]
	public ref class SingularValueDecompositionTest {
	public:

		[Test]
		void TestMGreaterThanN() {

			double a00 = 1;
			double a01 = -1;
			double a10 = -2;
			double a11 = 2;
			double a20 = 2;
			double a21 = -2;

			Matrix^ mat = gcnew Matrix(3, 2);
			mat[0, 0] = a00;
			mat[0, 1] = a01;

			mat[1, 0] = a10;
			mat[1, 1] = a11;

			mat[2, 0] = a20;
			mat[2, 1] = a21;

			/*Console::WriteLine(mat->ToString());*/

			Matrix^ sigma;
			Matrix^ U;
			Matrix^ VT;

			SingularValueDecomposition::Decompose(mat, sigma, U, VT);

			/*Console::WriteLine(sigma->ToString());
			Console::WriteLine(U->ToString());
			Console::WriteLine(VT->ToString());*/

			Matrix^ actual = U * sigma * VT;

			/*Console::WriteLine(actual->ToString());*/

			Assert::AreEqual(a00, actual[0, 0], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a01, actual[0, 1], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a10, actual[1, 0], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a11, actual[1, 1], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a20, actual[2, 0], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a21, actual[2, 1], 1e-10, "Test: SingularValueDecomposition");

		};

		[Test]
		void TestMLesserThanN() {

			double a00 = 4; 
			double a01 = 11;
			double a02 = 14;
			double a10 = 8;
			double a11 = 7;
			double a12 = -2;

			Matrix^ mat = gcnew Matrix(2, 3);
			mat[0, 0] = a00;  
			mat[0, 1] = a01;
			mat[0, 2] = a02;

			mat[1, 0] = a10;
			mat[1, 1] = a11;
			mat[1, 2] = a12;

			Matrix^ U;
			Matrix^ sigma;
			Matrix^ VT;

			SingularValueDecomposition::Decompose(mat, sigma, U, VT);

			Matrix^ actual = U * sigma * VT;

			Assert::AreEqual(a00, actual[0, 0], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a01, actual[0, 1], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a02, actual[0, 2], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a10, actual[1, 0], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a11, actual[1, 1], 1e-10, "Test: SingularValueDecomposition");
			Assert::AreEqual(a12, actual[1, 2], 1e-10, "Test: SingularValueDecomposition");

		}

		[Test]
		void TestSVD() {

			int rownum = 6;
			int colnum = 4;
			Matrix^ expected = gcnew Matrix(rownum, colnum);
			expected[0, 0] = 2.27;  
			expected[0, 1] = -1.54;
			expected[0, 2] = 1.15;
			expected[0, 3] = -1.94;

			expected[1, 0] = 0.28;  
			expected[1, 1] = -1.67;
			expected[1, 2] = 0.94;
			expected[1, 3] = -0.78;

			expected[2, 0] = -0.48;  
			expected[2, 1] = -3.09;
			expected[2, 2] = 0.99;
			expected[2, 3] = -0.21;

			expected[3, 0] = 1.07;  
			expected[3, 1] = 1.22;
			expected[3, 2] = 0.79;
			expected[3, 3] = 0.63;

			expected[4, 0] = -2.35;  
			expected[4, 1] = 2.93;
			expected[4, 2] = -1.45;
			expected[4, 3] = 2.30;

			expected[5, 0] = 0.62;  
			expected[5, 1] = -7.39;
			expected[5, 2] = 1.03;
			expected[5, 3] = -2.57;

			Matrix^ mat = expected->Clone();

			//Console::WriteLine(mat->ToString());

			Matrix^ U;
			Matrix^ sigma;
			Matrix^ VT;

			SingularValueDecomposition::Decompose(mat, sigma, U, VT);

			Matrix^ actual = U * sigma * VT;

			//Console::WriteLine(sigma->ToString());
			//Console::WriteLine(U->ToString());
			//Console::WriteLine(VT->ToString());
			//Console::WriteLine(actual->ToString());

			//Console::WriteLine(mat->ToString());

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(expected[i, j], actual[i, j], 1e-10, "Test: SingularValueDecomposition");
				}
			}

		};

		[Test]
		void TestComplexSVD() {

			int rownum = 6;
			int colnum = 4;

			ComplexMatrix^ expected = gcnew ComplexMatrix(rownum, colnum);
			
			expected[0, 0] = Complex(0.96, -0.81);  
			expected[0, 1] = Complex(-0.03, 0.96);
			expected[0, 2] = Complex(-0.91, 2.06);
			expected[0, 3] = Complex(-0.05, 0.41);

			expected[1, 0] = Complex(-0.98, 1.98);  
			expected[1, 1] = Complex(-1.20, 0.19);
			expected[1, 2] = Complex(-0.66, 0.42);
			expected[1, 3] = Complex(-0.81, 0.56);

			expected[2, 0] = Complex(0.62, -0.46);  
			expected[2, 1] = Complex(1.01, 0.02);
			expected[2, 2] = Complex(0.63, -0.17);
			expected[2, 3] = Complex(-1.11, 0.60);

			expected[3, 0] = Complex(-0.37, 0.38);  
			expected[3, 1] = Complex(0.19, -0.54);
			expected[3, 2] = Complex(-0.98, -0.36);
			expected[3, 3] = Complex(0.22, -0.20);

			expected[4, 0] = Complex(0.83, 0.51);  
			expected[4, 1] = Complex(0.20, 0.01);
			expected[4, 2] = Complex(-0.17, -0.46);
			expected[4, 3] = Complex(1.47, 1.59);

			expected[5, 0] = Complex(1.08, -0.28);  
			expected[5, 1] = Complex(0.20, -0.12);
			expected[5, 2] = Complex(-0.07, 1.23);
			expected[5, 3] = Complex(0.26, 0.26);

			ComplexMatrix^ mat = expected->Clone();

			/*Console::WriteLine(mat->ToString());*/

			ComplexMatrix^ U;
			ComplexMatrix^ sigma;
			ComplexMatrix^ VT;

			SingularValueDecomposition::Decompose(mat, sigma, U, VT);

			ComplexMatrix^ actual = U * sigma * VT;

			/*Console::WriteLine(sigma->ToString());
			Console::WriteLine(U->ToString());
			Console::WriteLine(VT->ToString());
			Console::WriteLine(actual->ToString());

			Console::WriteLine(mat->ToString());*/

			for (int i = 0; i < rownum; i++) {
				for (int j = 0; j < colnum; j++) {
					Assert::AreEqual(expected[i, j].Real, actual[i, j].Real, 1e-10, "Test: SingularValueDecomposition");
					Assert::AreEqual(expected[i, j].Imag, actual[i, j].Imag, 1e-10, "Test: SingularValueDecomposition");
				}
			}

		};

	};
}
